package com.xiao.filter;

import com.alibaba.fastjson.JSON;
import com.xiao.domain.ResponseResult;
import com.xiao.domain.constants.RedisConstants;
import com.xiao.enums.AppHttpCodeEnum;
import com.xiao.domain.pojo.LoginUser;
import com.xiao.utils.JwtUtil;
import com.xiao.utils.RedisCache;
import com.xiao.utils.WebUtils;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

@Component// 过滤器
/**
 * 为了在 Spring Security 中集成 JWT 认证机制，它简化了认证过程，允许无状态的客户端通过携带 token 来访问受保护的资源
 * 只在单次请求中有效 [为了安全，请求结束后销毁]
 * 所有请求都会经过过滤器
 */
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private RedisCache redisCache;

    /**
     * 1.解析前端传递 token
     * 2.获取到用户 ID
     * 3.通过用户 ID 到 redis 中查询
     * 4. 查询到，说明没有过期--->true 存入登录用户对象到 securityContext 上下文，提供后续使用
     *    查询不到，说明过期--->false
     *  【只要为 其中一环有问题,都不能通过过滤器】
     *  【因此 如果需要用到 登录用户信息的,请求中必须携带 token】
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {



        System.out.println("------------ JwtAuthenticationTokenFilter Start ------------");
        /** 1. 获取请求头中的 token
         * 只要涉及到 敏感的用户操作都要设置 token ，要不然也没法获取到当前登录用户的信息
         * 因此我们需要用户信息的时候，都需要 传递 token ，从 （LoginUser）SecurityContextHolder.getContext().getAuthentication().getPrincipal() 获取我们存放的登录用户
         */
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)){
            this.log();
            // 如果 token 为空;说明该接口不需要登录，直接放行
            filterChain.doFilter(request,response);
            return;
        }
        // 2. 解析获取 userId
        Claims claims =null;
        try {
             claims = JwtUtil.parseJWT(token);
        } catch (Exception e) {
            e.printStackTrace();
            // token 超时 token 非法
            //响应告诉前端需要重新登录
            ResponseResult responseResult = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(response, JSON.toJSONString(responseResult));
            this.log();
            return;
        }
        String userId = claims.getSubject();
        // 3. 从 Redis 中获取用户信息
        LoginUser loginUser = redisCache.getCacheObject(RedisConstants.ADMIN_LOGIN_PREFIX + userId);
        // 4. 如果获取结果为空
        if (Objects.isNull(loginUser)){
            ResponseResult responseResult = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
            WebUtils.renderString(response,JSON.toJSONString(responseResult));

            this.log();
            return;
        }
        // 5. 存入 SecurityContextHolder
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, null);
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        /**
         * 存放之后，我们就可以从 上下文中获取 Principal 即 LoginUser
         * LoginUser user = (LoginUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
         */


        this.log();
        filterChain.doFilter(request,response);

    }
    public void log(){
        System.out.println("------------ JwtAuthenticationTokenFilter End ------------");
    }
}
